/*NOTICE

This software was produced for the U. S. Government
under Contract No. FA8721-09-C-0002, and is
subject to the Rights in Noncommercial Computer Software
and Noncommercial Computer Software Documentation Clause
(DFARS) 252.227-7014 (JUN 1995)

(c) 2009 The MITRE Corporation. All Rights Reserved.
*/
package org.mitre.xmltransform.uml13tospmif;

import java.io.File;

//import org.mitre.xmltransform.spmif.ComboStep;
import org.mitre.xmltransform.spmif.ExecutionStep;
//import org.mitre.xmltransform.spmif.ExternalReferenceStep;
import org.mitre.xmltransform.spmif.HierarchyStep;
import org.mitre.xmltransform.spmif.SpmifOutputDocument;
import org.mitre.xmltransform.spmif.StepState;
import org.mitre.xmltransform.transform.Transform;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


/*
 * To Test:
 * C:\Documents and Settings\gquinn\workspace\XmlTransformer\bin>java -mx128m -cp . org.mitre.xmltransform.XmlTransformer "C:\Documents and Settings\gquinn\workspace\UML2SPMIF\E10.xml" Uml13ToSpmif "C:\working\PerfEng\generated\E10_Java_SPMIF.xml"
 */

public class Uml13ToSpmifTransform extends Transform {

	@Override
	public void initialize(File pInputFile, File pOutputFile) {
		mInput = new Uml13InputDocument(pInputFile);
		mOutput = new SpmifOutputDocument(pOutputFile);
	}

	@Override
	public void transform() {
		System.out.println("transform input");
		if (generateOutput()) {
			mOutput.finishDocument();
			System.out.println("write output");
			mOutput.writeOutput();
		}		
	}

	private boolean generateOutput() {
		try {
			mOutput.createProjectElement(mInput.getInputValue("/XMI/XMI.content/UML:Model/@name"));
			
			//TODO interactions for 1.3...
			NodeList interactions = mInput.getInputNodes("//UML:Collaboration");
			if (interactions == null) return true;
			Node n;
			System.out.println("Found " + interactions.getLength() + " collaborations.");
			for (int i = 0; i < interactions.getLength(); i++) {
				n = interactions.item(i);
				generatePerformanceScenariosForInteraction(n);
			}
			
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	private void generatePerformanceScenariosForInteraction(Node pInteraction) {
		String name = mInput.getInputValue(pInteraction, "@name");		
		Element perfScenarioElement = mOutput.createPerformanceScenario(name, mInput.getFileName(), null, null, null);
		
		NodeList nodes = mInput.getInputNodes(pInteraction, "UML:Collaboration.interaction/UML:Interaction/UML:Interaction.message/*");
		Element executionGraphElement = mOutput.createExecutionGraph(perfScenarioElement, null, name, true);
		StepState state = new StepState(perfScenarioElement, executionGraphElement, name, null, null);
		generateExecutionGraph(	nodes, 0, state);
	}

	protected String buildEventOccurrenceNodeName(Node pEventOccurrence) {
		StringBuffer result = new StringBuffer();

		mInput.setVariable("modelId", mInput.getInputValue("//XMI.content/UML:Model/@xmi.id"));
		mInput.setVariable("messageId", mInput.getInputValue(pEventOccurrence, "@xmi.id"));
		mInput.setVariable("actionId", mInput.getInputValue(pEventOccurrence, "@action"));
		mInput.setVariable("receiverId", mInput.getInputValue(pEventOccurrence, "@receiver"));		
		mInput.setVariable("senderId", mInput.getInputValue(pEventOccurrence, "@sender"));
		mInput.setVariable("receiverClassId", mInput.getInputValue(pEventOccurrence, "ancestor::UML:Collaboration/UML:Namespace.ownedElement/UML:ClassifierRole[@xmi.id=$receiverId]/@base"));
		String messageName = mInput.getInputValue(pEventOccurrence, "@name");
		String receiverClassName = mInput.getInputValue(pEventOccurrence, "ancestor::UML:Namespace.ownedElement/descendant::UML:Class[@xmi.id=$receiverClassId]/@name");
		String receiverInstanceName = mInput.getInputValue(pEventOccurrence, "ancestor::UML:Collaboration/UML:Namespace.ownedElement/UML:ClassifierRole[@xmi.id=$receiverId]/@name");
		String name = receiverClassName;
		if (receiverClassName.length() == 0) {
			name = receiverInstanceName;
		}
		if (messageName.length() == 0) {
			if (mInput.evaluateBooleanInputExpression(pEventOccurrence, "ancestor::UML2:Interaction/UML2:Interaction.message/UML2:Message[@xmi.id=$messageId]/UML:ModelElement.stereotype")) {
				mInput.setVariable("stereotypeId", mInput.getInputValue(pEventOccurrence, "ancestor::UML2:Interaction/UML2:Interaction.message/UML2:Message[@xmi.id=$messageId]/UML:ModelElement.stereotype/UML:Stereotype/@xmi.idref"));
				String stereotypeName = mInput.getInputValue(pEventOccurrence, "/XMI/XMI.content/UML:Model[@xmi.id=$modelId]/UML:Namespace.ownedElement/UML:Stereotype[@xmi.id=$stereotypeId]/@name");
				result.append(stereotypeName);
			} else {
				result.append("anon");
			}
			result.append("_");
			result.append(name);
		} else {
			result.append(messageName);
			/*NodeList messages = mInput.getInputNodes(pEventOccurrence, "ancestor::UML2:Interaction/UML2:Interaction.message/UML2:Message[@xmi.id=$messageId]/UML2:Message.argument/UML2:OpaqueExpression[string-length(@body) > 0]");
			Node n;
			for (int i = 0; i < messages.getLength(); i++) {
				n = messages.item(i);
				result.append("_");
				result.append(mInput.getInputValue(n, "@body"));
			}*/
			result.append("_");
			result.append(name);
		}
		//let's put a the node number of the UML2:EventOccurrence within the context of the entire XMI 
		result.append("_");
		NodeList eventOccurrences = mInput.getInputNodes("//UML:Message");
		Node eo;
		String xmiId;
		String nodeId = mInput.getInputValue(pEventOccurrence, "@xmi.id");
		for (int i = 0; i < eventOccurrences.getLength(); i++) {
			eo = eventOccurrences.item(i);
			xmiId = mInput.getInputValue(eo, "@xmi.id");
			if (xmiId.equals(nodeId)) {
				result.append((i+1));
				break;
			}
		}
//		mInput.resetVariables();
		return result.toString();
	}

	private int generateExecutionGraph(NodeList pFragments, int pStartIndex, StepState pState) {
		//Element newExecutionGraphElement, newPerformanceScenarioElement;

		// build hierarchy
		HierarchyStep newStep;
		//NodeList combinedFragmentInteractionOperands;
		Node node;// ,interactionOperand;
		//String newExecutionGraphName;
		String id, execId;//, startExecutionLocation;
		int endIndex = pFragments.getLength();
		
		for (int i = pStartIndex; i < pFragments.getLength(); i++) {
			node = pFragments.item(i);
			id = mInput.getInputValue(node, "@xmi.id");
			newStep = null;
			if (mInput.evaluateBooleanInputExpression(node, "boolean(self::UML:Message)")) {
					System.out.println("    START");
					execId = mInput.getInputValue(node, "UML:Message/@xmi.id");
					newStep = new ExecutionStep(id, execId, node, buildEventOccurrenceNodeName(node), mInput, mOutput);
					pState.addParentStep(newStep);
					System.out.println("    FINISH");
					//execId = mInput.getInputValue(node, "UML2:EventOccurrence.finishExec/UML2:ExecutionOccurrence/@xmi.idref");
					if (!pState.closeParentStep(execId)) {
						//System.exit(0);
					}
			}
			pState.addToStepMap(id, newStep);
		}
		
		//if (pIsMain && !pState.isValidEndState()) {
		if (!pState.isValidEndState()) {
			throw new IllegalArgumentException("Failed to close all parent steps.");
		}
		
		// start node
		if (pState.getNumberOfRootSteps() > 0) {
			HierarchyStep startStep = pState.getRootStep(0);
			pState.getExecutionGraphElement().setAttribute("StartNode", startStep.getStartNodeName());
		} else {
			pState.getExecutionGraphElement().setAttribute("StartNode", "");
		}
				
		// nodes
		HierarchyStep step;
		for (int i = 0; i < pState.getNumberOfRootSteps(); i++) {
			step = pState.getRootStep(i);
			step.generateStep(pState.getExecutionGraphElement(), pState.getLocation());
		}
		
		// arcs
		String fromNode = null;
		for (int i = 0; i < pState.getNumberOfRootSteps(); i++) {
			step = pState.getRootStep(i);
			fromNode = step.generateArcs(pState.getExecutionGraphElement(), fromNode);
		}
		return endIndex;
	}

	private Uml13InputDocument mInput;
	private SpmifOutputDocument mOutput;
}
